#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/init.h> /* Needed for the macros */
#include <linux/kernel.h> /* Needed for pr_info() */
#include <linux/module.h> /* Needed by all modules */
#include <linux/timer.h>

//sudo mknod /dev/mycdev001 c  403 0

/**
 *
 * need to get major number from /proc/devices
 * then
 * sudo mknod /dev/t01 c 238 0



 https://www.kernel.org/doc/html/v4.12/driver-api/device-io.html



 sudo cat /proc/iomem

 ff720000-ff7200ff : /pinctrl/gpio0@ff720000
 ff730000-ff7300ff : /pinctrl/gpio1@ff730000
 ff780000-ff7800ff : /pinctrl/gpio2@ff780000
 ff788000-ff7880ff : /pinctrl/gpio3@ff788000
 ff790000-ff7900ff : /pinctrl/gpio4@ff790000


 RK3399 has 5 groups of GPIO banks: GPIO0~GPIO4, and each group is distinguished by numbers A0~A7, B0~B7, C0~C7, D0~D7.

 地址：
 GPIO0 ： FF72_0000
 GPIO1 ： FF73_0000
 GPIO2 ： FF78_0000
 GPIO3 ： FF78_8000
 GPIO4 ： FF79_0000

 we use GPIO_1_A7 pin


 Offset
 GPIO_SWPORTA_DR  : 0x0000
 GPIO_SWPORTA_DDR : 0x0004


GRF_GPIO2A_IOMUX 0x0e000 W 0x00000000 GPIO2A iomux control
GRF_GPIO2B_IOMUX 0x0e004 W 0x00000000 GPIO2B iomux control
GRF_GPIO2C_IOMUX 0x0e008 W 0x00000000 GPIO2C iomux control
GRF_GPIO2D_IOMUX 0x0e00c W 0x00000000 GPIO2D iomux control
GRF_GPIO3A_IOMUX 0x0e010 W 0x00000000 GPIO3A iomux control
GRF_GPIO3B_IOMUX 0x0e014 W 0x00000000 GPIO3B iomux control
GRF_GPIO3C_IOMUX 0x0e018 W 0x00000000 GPIO3C iomux control
GRF_GPIO3D_IOMUX 0x0e01c W 0x00000000 GPIO3D iomux control
GRF_GPIO4A_IOMUX 0x0e020 W 0x00000000 GPIO4A iomux control
GRF_GPIO4B_IOMUX 0x0e024 W 0x00000000 GPIO4B iomux control
GRF_GPIO4C_IOMUX 0x0e028 W 0x00000000 GPIO4C iomux control
GRF_GPIO4D_IOMUX 0x0e02c W 0x00000000 GPIO4D iomux control



GPIO1 Interface

gpio1_porta[7:0] I/O GPIO1_A[7:0] GRF_GPIO1A_IOMUX[15:0]=16’h0
gpio1_porta[9:8] I/O GPIO1_B[1:0] GRF_GPIO1B_IOMUX[3:0]=4’h0

GPIO2 Interface

gpio2_porta[7:0] I/O GPIO2_A[7:0] GRF_GPIO2A_IOMUX[15:0]=16’h0
gpio2_porta[15:8] I/O GPIO2_B[7:0] GRF_GPIO2B_IOMUX[15:0]=16’h0
gpio2_porta[23:16] I/O GPIO2_C[7:0] GRF_GPIO2C_IOMUX[15:0]=16’h0
gpio2_porta[31:24] I/O GPIO2_D[7:0] GRF_GPIO2D_IOMUX[15:0]=16’h0

GPIO3 Interface
gpio3_porta[4:0] I/O GPIO3_A[4:0] GRF_GPIO3A_IOMUX[9:0]=10’h0
gpio3_porta[15:8] I/O GPIO3_B[7:0] GRF_GPIO3B_IOMUX[15:0]=16’h0
gpio3_porta[22:16] I/O GPIO3_C[6:0] GRF_GPIO3C_IOMUX[13:0]=14’h0

GPIO4 Interface
gpio4_porta[23:16] I/O GPIO4_C[7:0] GRF_GPIO4C_IOMUX[15:0]=16’h0


 *
 */

#define MY_MAJOR       499

#define  GRF_BASE    0xFF770000

#define GRF_GPIO3D_IOMUX (0x0e01c + GRF_BASE)

#define GPIO_1_BASE  0xFF730000

//#define GPIO_2_BASE  0xFF780000

//#define GPIO_3_BASE  0xFF788000

//#define GPIO_4_BASE  0xFF790000

#define GPIO_BASE  GPIO_1_BASE

//#define  GRF_GPIO2B_IOMUX  (GRF_BASE + 0x0e004)

#define  GPIO1_SWPORTA_DR GPIO_BASE

#define  GPIO1_SWPORTA_DDR  (GPIO_BASE + 0x0004)

#define  GPIO1_INTEN  (GPIO_BASE + 0x0030)

#define  GPIO_INTMASK  (GPIO_BASE + 0x0034)

#define  GPIO_DEBOUNCE  (GPIO_BASE + 0x0048)

#define  GPIO_EXT_PORTA  (GPIO_BASE + 0x0050)

//#define  USETIMER

#define log pr_warning

static void my_timer_func(unsigned long);


#ifdef USETIMER
int LOCATION   = 7;
#else
#define LOCATION 7
#endif

#define MY_MAX_MINORS  2

#define   DEVICE_NAME  "device name 499"

struct my_device_data {
	struct cdev cdev;
	char *desc;
};

struct my_device_data devs[MY_MAX_MINORS];
unsigned int __iomem *data;
unsigned int __iomem *direction;
unsigned int __iomem *interrupt;
unsigned int __iomem *interrupt_mask;
unsigned int __iomem *debounce;
unsigned int __iomem *ext_porta;

static inline void on(void) {
	pr_info("on %d \n", LOCATION);
	writel(readl(direction) | (0x1 << LOCATION), direction);
	writel(readl(data) | (0x1 << LOCATION), data);
//	writel(readl(ext_porta) | (0x1 << LOCATION), ext_porta);
}

static inline void off(void) {
	writel(readl(direction) & (~(0x1 << LOCATION)), direction);
	writel(readl(data) & (~(0x1 << LOCATION)), data);
}

static long my_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
	pr_info("my_ioctl,cmd : %d\n", cmd);
	switch (cmd) {
	// on
	case 0: {
		on();
		break;
	}
		// off
	case 1: {
		off();
		break;
	}
	}
	return 0;
}
const struct file_operations my_fops = { .owner = THIS_MODULE, .unlocked_ioctl =
		my_ioctl };

static dev_t mydev;

static DEFINE_TIMER(my_timer, my_timer_func, 0, 0);


void  read(void){
	 u32 d;
		 u32 d2;
	d = 	readl(direction);
		pr_warn("direction := 0x%x\n",d);
		pr_warn("direction := %d\n", (d & (1 << LOCATION)) >> LOCATION);

		d2=readl(data);
		pr_warn("data :=  0x%x\n",d2);
		pr_warn("data := %d\n", (d2 & (1 << LOCATION)) >> LOCATION);




}


static __init int my_init(void) {
	int err;
	int i;
	 u32 d;
	 u32 d2;
	 u32 d3;
	 u32 d4;
	 u32 d5;
	pr_info("a3 init_module\n");err = alloc_chrdev_region(&mydev, 0, MY_MAX_MINORS, DEVICE_NAME);
	if (err != 0) {
		return err;
	}

	for (i = 0; i < MY_MAX_MINORS; i++) {
		/* initialize devs[i] fields */
		cdev_init(&devs[i].cdev, &my_fops);
		cdev_add(&devs[i].cdev, MKDEV(MAJOR(mydev), i)
, 1);
	}

	direction = ioremap(GPIO1_SWPORTA_DDR, 4);
	data = ioremap(GPIO1_SWPORTA_DR, 4);
	interrupt = ioremap(GPIO1_INTEN, 4);
	interrupt_mask = ioremap(GPIO_INTMASK, 4);
	debounce = ioremap(GPIO_DEBOUNCE, 4);
	ext_porta = ioremap(GPIO_EXT_PORTA, 4);

	d = 	readl(direction);
	pr_warn("direction := 0x%x\n",d);
	pr_warn("direction := %d\n", (d & (1 << LOCATION)) >> LOCATION);

	d2=readl(data);
	pr_warn("data :=  0x%x\n",d2);
	pr_warn("data := %d\n", (d2 & (1 << LOCATION)) >> LOCATION);

	d3=readl(interrupt);
//	pr_warn("interrupt :=  0x%x\n",d37);
//	pr_warn("interrupt := %d\n", (d3 & (1 << LOCATION)) >> LOCATION);

	d4=readl(interrupt_mask);
//	pr_warn("interrupt_mask :=  0x%x\n",d4);
//	pr_warn("interrupt_mask := %d\n", (d4 & (1 << LOCATION)) >> LOCATION);

	d5=readl(debounce);
//	log("debounce :=  0x%x\n",d5);
//	log("debounce := %d\n", (d5 & (1 << LOCATION)) >> LOCATION);

	d5=readl(ext_porta);
//	log("ext_porta :=  0x%x\n",d5);
//	log("ext_porta := %d\n", (d5 & (1 << LOCATION)) >> LOCATION);



//	GRF_GPIO2B_IOMUX

		on();
//		read();
//#ifdef USETIMER
//		mod_timer(&my_timer, jiffies + 2 * HZ);
//#endif

	return 0;
}

static void __exit my_exit(void) {
	int i;
	del_timer(&my_timer);
	off();
	iounmap(data);
	iounmap(direction);
	pr_info("a3 cleanup_module\n");
	for (i = 0; i < MY_MAX_MINORS; i++) {
		/* release devs[i] fields */
		cdev_del(&devs[i].cdev);
	}
	unregister_chrdev_region(mydev, MY_MAX_MINORS);
}

static void my_timer_func(unsigned long unused){
	pr_info("timer expires , now  do sth\n");
	off();
	#ifdef USETIMER
		LOCATION++;
	#endif

	on();
	if(!timer_pending(&my_timer)){
		mod_timer(&my_timer, jiffies + 2 * HZ);
	}
}


module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andy");
MODULE_DESCRIPTION("A sample c  driver");
